package test.logic.dfm;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import cfsmon.common.Event;
import cfsmon.common.Parameter;
import cfsmon.core.LogConfig;
import cfsmon.logic.dfm.Dfm;
import cfsmon.logic.dfm.DfmOptimizer;
import cfsmon.logic.dfm.DfmState;
import cfsmon.logic.dfm.IDfm;

public class DfmOptimizer01 {

	@BeforeClass
	public static void classSetUp(){
		Logger.getRootLogger().removeAllAppenders();
		Logger.getRootLogger().addAppender(new ConsoleAppender(LogConfig.cfsmonLogConsoleLayout));
	}
	
	IDfm dfm;

	@Before
	public void setUp() throws Exception {
		dfm = new Dfm();
	}

	@After
	public void tearDown() throws Exception {
		dfm = null;
	}

	@Test
	public void testNull() {
		assertNull(DfmOptimizer.optimize(null));
	}
	
	@Test
	public void testTransitionsFromFinal(){
		DfmState[] d = getSomeStates(3);
		Event[] e = getSomeEvents(1);
		
		d[0].putTransition(e[0], d[1]);
		d[1].putTransition(e[0], d[2]);
		
		dfm.addEvent(e[0]);
		dfm.setInitialState(d[0]);
		dfm.addFinalState(d[1]);
		dfm.addState(d[2]);
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertEquals(dfmOpt.getStates().values().size(), 2);
		assertTrue(dfmOpt.getStates().get(d[1].getName()).getTransitions().keySet().isEmpty());
		
	}
	
	@Test
	public void testDefaultTransitions(){
		DfmState[] d = getSomeStates(4);
		Event[] e = getSomeEvents(2);
		
		d[0].putTransition(e[0], d[1]);
		d[0].setDefaultTransition(d[2]);
		d[1].putTransition(e[0], d[2]);
		d[1].setDefaultTransition(d[0]);
		d[2].setDefaultTransition(d[3]);
		d[3].setDefaultTransition(d[3]);// will be removed
		
		dfm.addEvent(e[0]);
		dfm.addEvent(e[1]);
		dfm.setInitialState(d[0]);
		dfm.addState(d[1]);
		dfm.addFinalState(d[2]);
		dfm.addFinalState(d[3]); // unreachable
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertEquals(3, dfmOpt.getStates().values().size());
		assertEquals(1, dfmOpt.getFinalStates().size());
		for(DfmState s : dfmOpt.getFinalStates()){
			assertTrue(s.getTransitions().keySet().isEmpty());
			assertEquals(s, s.getDefaultTransition());
		}
	}
	
	@Test
	public void testRemoveAllFinalStates(){
		/*
		 * The optimization will remove all final states. This should not lead to an error, but to a warning.
		 * */
		DfmState[] d = getSomeStates(2);
		Event[] e = getSomeEvents(1);
		
		d[0].putTransition(e[0], d[0]);
		d[0].setDefaultTransition(d[1]);
		d[1].setDefaultTransition(d[1]);
		
		dfm.addEvent(e[0]);
		dfm.setInitialState(d[0]);
		dfm.addFinalState(d[1]); // unreachable
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertNotNull(dfmOpt);
	}
	
	@Test
	public void testCreatingDefaultFail(){
		DfmState[] d = getSomeStates(3);
		Event[] e = getSomeEvents(2);
		
		e[0].parameters.add(new Parameter("int", "i"));
		d[0].putTransition(e[0], d[1]);
		d[0].setDefaultTransition(d[2]); // creating default transition
		d[1].setDefaultTransition(d[1]);
		d[2].setDefaultTransition(d[2]);
		
		dfm.addEvent(e[0]);
		dfm.addEvent(e[1]);
		dfm.setInitialState(d[0]);
		dfm.addFinalState(d[1]);
		dfm.addState(d[2]);
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertNull(dfmOpt);
	}
	
	@Test
	public void testNoEvents(){
		DfmState[] d = getSomeStates(2);
		
		d[0].setDefaultTransition(d[0]);
		
		dfm.setInitialState(d[0]);
		dfm.addFinalState(d[1]);
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertNotNull(dfmOpt);
	}
	
	@Test
	public void testNonCreatingEventFail(){
		DfmState[] d = getSomeStates(3);
		Event[] e = getSomeEvents(2);
		Parameter p = new Parameter("int", "i");
		e[0].parameters.add(p);
		d[0].putTransition(e[0], d[1]);
		d[0].putTransition(e[1], d[2]); // non-creating event transition
		d[1].setDefaultTransition(d[1]);
		d[2].setDefaultTransition(d[2]);
		
		dfm.addParameter(p);
		dfm.addEvent(e[0]);
		dfm.addEvent(e[1]);
		dfm.setInitialState(d[0]);
		dfm.addFinalState(d[1]);
		dfm.addState(d[2]);
		
		IDfm dfmOpt = DfmOptimizer.optimize(dfm);
		
		assertNull(dfmOpt);
	}
	
	private DfmState[] getSomeStates(int amount){
		DfmState[] d = new DfmState[amount];
		for (int i = 0; i < d.length; i++)
			d[i] = new DfmState("d" + i);
		return d;
	}
	
	private Event[] getSomeEvents(int amount){
		Event[] e = new Event[amount];
		for (int i = 0; i < e.length; i++)
			e[i] = new Event("e" + i);
		return e;
	}

}
